﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Xml.Linq;

using ProjectBase.Core;

namespace $safeprojectname$.Publishing
{
    public class MessagePublishingHandler : PublishingHandlerBase
    {
        #region members
        protected const string SuffixProxy = "Proxy";
        protected const string SuffixController = "Controller";
        protected const string SuffixFacade = "Facade";
        protected const string SuffixDao = "Dao";
        protected const string SuffixBuilder = "Builder";
        #endregion members

        #region Publish
        public override void Init(XElement settings) { }

        protected override PublishingResult Publish(object reporter, string message, Exception exception)
        {
            var holder = reporter as IMessagesHolder;
            if (holder.Is() // decision if publish at all
                && exception.Is())
            {
                var msg = AppendMessage(holder, message ?? exception.Message, Severity.Error);

                msg.DebugDetails = exception.ToString(); // details about reported exception
            }
            return PublishingResult.Continue;
        }

        protected override PublishingResult Publish(object reporter, Severity severity, object message, params object[] args)
        {
            var holder = reporter as IMessagesHolder;

            if (holder.Is() // decision if publish at all
                && message.Is()
                && !severity.Equals(Severity.Debug))
            {
                Exception exception; // Formatting exception - if any

                var msg = AppendMessage(holder, TryFormatMessage(out exception, holder, message, args), severity);

                msg.Template = message.ToString(); // the passed template
                msg.DebugDetails = exception.Is()  // details about formatting exception
                    ? exception.ToString()
                    : string.Empty;
            }
            return PublishingResult.Continue;
        }

        protected override PublishingResult Publish(object reporter, Func<string> message, Severity severity)
        {
            var holder = reporter as IMessagesHolder;

            if (holder.Is()  // decision if publish at all
                && message.Is()
                && !severity.Equals(Severity.Debug))
            {
                Exception exception; // invocation exception - if any

                var msg = AppendMessage(holder, TryInvokeMessage(out exception, message), severity);

                msg.DebugDetails = exception.Is() // details about invocation exception
                    ? exception.ToString()
                    : string.Empty;

            }
            return PublishingResult.Continue;
        }
        #endregion Publish

        #region protected

        protected virtual Message AppendMessage(IMessagesHolder holder, string messageText, Severity severity)
        {
            Contract.Requires(holder.Is());
            Contract.Requires(messageText.Is());

            string layerName;

            var message = new Message
            {
                Severity = severity,
                Reporter = GetReporterName(holder, out layerName),
                Layer = layerName,
                Text = messageText,
            };

            if (holder.Messages.IsNull())
            {
                holder.Messages = new Messages();
            }

            holder.Messages.Add(message);

            return message;
        }

        protected virtual string TryInvokeMessage(out Exception exception, Func<string> message)
        {
            Contract.Requires(message.Is());
            exception = null;
            try
            {
                return message.Invoke();
            }
            catch (Exception ex)
            {
                exception = ex;
                return "MessagePublishingHandler: A fatal error occurred, while trying to append message: '{0}', exception: {1}"
                    .FormatWith(message.ToString(), ex.Message);
            }
        }
        protected virtual string TryFormatMessage(out Exception exception, IMessagesHolder holder, object message, params object[] args)
        {
            Contract.Requires(message.Is());

            exception = null;
            var template = message.ToString();
            string area;
            GetReporterName(holder, out area);

            try
            {
                return template.IsEmpty()
                    ? string.Empty
                    : template.GetLocalized(area).FormatWith(args);
            }
            catch (Exception ex)
            {                   // problems with formatting
                exception = ex; // problems with localizaton
            }                   // any other problems cannot break this

            try
            {
                return template.FormatWith(args);
            }
            catch (Exception ex)
            {
                exception = ex;
                return "MessagePublishingHandler: A fatal error occurred, while trying to format template: '{0}', exception: {1}"
                    .FormatWith(message, ex.Message);
            }
        }
        protected virtual string GetReporterName(object reporter, out string layerName)
        {
            layerName = string.Empty;
            if (reporter.IsNull())
            {
                return string.Empty;
            }

            var reporterName = reporter.GetType().Name;
            layerName = reporter.GetType().Namespace;

            if (reporterName.EndsWith(SuffixProxy, StringComparison.OrdinalIgnoreCase))
            {
                reporterName = reporterName.Remove(reporterName.Length - SuffixProxy.Length);
            }

            if (reporterName.EndsWith(SuffixController, StringComparison.OrdinalIgnoreCase))
            {
                layerName = SuffixController;
                return reporterName.Remove(reporterName.Length - SuffixController.Length);
            }
            if (reporterName.EndsWith(SuffixBuilder, StringComparison.OrdinalIgnoreCase))
            {
                layerName = SuffixBuilder;
                return reporterName.Remove(reporterName.Length - SuffixBuilder.Length);
            }
            if (reporterName.EndsWith(SuffixDao, StringComparison.OrdinalIgnoreCase))
            {
                layerName = SuffixDao;
                return reporterName.Remove(reporterName.Length - SuffixDao.Length);
            }
            if (reporterName.EndsWith(SuffixFacade, StringComparison.OrdinalIgnoreCase))
            {
                layerName = SuffixFacade;
                return reporterName.Remove(reporterName.Length - SuffixFacade.Length);
            }
            return reporterName;
        }
        #endregion protected
    }
}